{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        },
        "colab_type": "code",
        "id": "JtwRxl7vxkf3"
      },
      "outputs": [],
      "source": [
        "import random\n",
        "\n",
        "import gin\n",
        "\n",
        "\n",
        "# When using Gin interactively, reregistering a function is not an error.\n",
        "gin.enter_interactive_mode()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "lAnlZkMkxt2Z"
      },
      "source": [
        "### Defining \"configurables\"\n",
        "\n",
        "Any function or class can be decorated with `@gin.configurable`, making it possible to provide or\n",
        "override default values for its parameters using Gin. Gin uses a simple **`function_name.parameter_name = value`** syntax to \"bind\" values to parameters."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "height": 68
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 221,
          "status": "ok",
          "timestamp": 1529004187958,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "k8CX-4d7ySXs",
        "outputId": "f8efe596-acf5-4df6-8f7a-2e2ee416f14d"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Hello world!\n",
            "Hello Gin!\n",
            "Hello world!\n"
          ]
        }
      ],
      "source": [
        "gin.clear_config()\n",
        "\n",
        "@gin.configurable\n",
        "def say_hello(name=\"world\"):\n",
        "  print(\"Hello %s!\" % name)\n",
        "\n",
        "# Decorated functions or classes preserve their default behavior.\n",
        "say_hello()\n",
        "\n",
        "# Bindings are usually specified in a file, e.g., \"config.gin\". For simplicity\n",
        "# here we pass a string directly to `gin.parse_config`.\n",
        "gin.parse_config(\"\"\"\n",
        "say_hello.name = \"Gin\"\n",
        "\"\"\")\n",
        "\n",
        "# With the above config, the \"name\" parameter now defaults to \"Gin\".\n",
        "say_hello()\n",
        "\n",
        "# But the caller can always override it.\n",
        "say_hello(\"world\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mSb0IrBy7Y4p"
      },
      "source": [
        "Any Python literal is an acceptable \"value\" in a Gin binding. Classes can be made configurable too."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "height": 51
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 185,
          "status": "ok",
          "timestamp": 1529005106578,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "ssT1MoS93EH3",
        "outputId": "a7ac1025-ad96-4133-b027-0b8db3d3b302"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "item\n",
            "2\n"
          ]
        }
      ],
      "source": [
        "gin.clear_config()\n",
        "\n",
        "@gin.configurable\n",
        "class Picker(object):\n",
        "  # Bindings affect the constructor of the class.\n",
        "  def __init__(self, items):\n",
        "    self._items = items\n",
        "\n",
        "  def pick(self):\n",
        "    print(random.choice(self._items))\n",
        "\n",
        "Picker(['item']).pick()\n",
        "\n",
        "gin.parse_config(\"\"\"\n",
        "Picker.items = ['one', 2, ((), (), ()),]\n",
        "\"\"\")\n",
        "\n",
        "Picker().pick()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "dIrRX-UAxBr6"
      },
      "source": [
        "When calling a function where arguments are expected to be supplied by Gin, passing the value `gin.REQUIRED` clearly documents this expectation in the code."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "height": 136
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 171,
          "status": "ok",
          "timestamp": 1529005109279,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "_WBVKryvxKmJ",
        "outputId": "b9aa7fb5-5d1c-4b3b-d31b-2f74405f1c39"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Error message (missing arg): __init__() takes exactly 2 arguments (1 given)\n",
            "  No values supplied by Gin or caller for arguments: ['items']\n",
            "  Gin had values bound for: []\n",
            "  Caller supplied values for: ['self']\n",
            "  In call to configurable 'Picker' (\u003cunbound method Picker.__init__\u003e)\n",
            "Error message (gin.REQUIRED): Required bindings for `Picker` not provided in config: ['items']\n",
            "Error message (gin.REQUIRED): Required bindings for `say_hello` not provided in config: ['name']\n"
          ]
        }
      ],
      "source": [
        "gin.clear_config()\n",
        "\n",
        "# Calling Picker with a missing argument yields an informative but somewhat\n",
        "# verbose error message.\n",
        "try:\n",
        "  Picker()\n",
        "except TypeError as e:\n",
        "  print('Error message (missing arg):', e)\n",
        "\n",
        "# We can use gin.REQUIRED to indicate we expect the value to be supplied by Gin.\n",
        "# This improves the readability of the code and clarifies the error message.\n",
        "try:\n",
        "  Picker(gin.REQUIRED)\n",
        "except RuntimeError as e:\n",
        "  print('Error message (gin.REQUIRED):', e)\n",
        "\n",
        "# gin.REQUIRED can also be used for keyword arguments.\n",
        "try:\n",
        "  say_hello(name=gin.REQUIRED)\n",
        "except RuntimeError as e:\n",
        "  print('Error message (gin.REQUIRED):', e)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "BsrkuaW21ReO"
      },
      "source": [
        "### Passing \"references\"\n",
        "\n",
        "Gin allows \"references\" to registered functions or classes to be used as values, via the `@fn_or_class` syntax."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "height": 34
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 161,
          "status": "ok",
          "timestamp": 1528959274391,
          "user": {
            "displayName": "Dan Holtmann-Rice",
            "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s128",
            "userId": "110473418774139951293"
          },
          "user_tz": 240
        },
        "id": "nRCf5Gx61Wjj",
        "outputId": "e300a847-d11b-4142-8060-0b970e153104"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "arg = \u003cfunction return_a_value at 0xc2457d0\u003e\n"
          ]
        }
      ],
      "source": [
        "gin.clear_config()\n",
        "\n",
        "@gin.configurable\n",
        "def return_a_value():\n",
        "  return 'fn1_return'\n",
        "\n",
        "@gin.configurable\n",
        "def print_arg(arg):\n",
        "  print('arg = %r' % arg)\n",
        "\n",
        "gin.parse_config(\"\"\"\n",
        "print_arg.arg = @return_a_value\n",
        "\"\"\")\n",
        "print_arg(gin.REQUIRED)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "hzZ7cvAQ2ZRQ"
      },
      "source": [
        "References can be \"evaluated\", using the syntax `@fn_or_class()`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "height": 34
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 148,
          "status": "ok",
          "timestamp": 1528959274590,
          "user": {
            "displayName": "Dan Holtmann-Rice",
            "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s128",
            "userId": "110473418774139951293"
          },
          "user_tz": 240
        },
        "id": "UaZK9sVy2giY",
        "outputId": "65d68e57-e18e-44be-9835-25ee7af5c5e5"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "arg = 'fn1_return'\n"
          ]
        }
      ],
      "source": [
        "gin.parse_config(\"\"\"\n",
        "print_arg.arg = @return_a_value()\n",
        "\"\"\")\n",
        "print_arg(gin.REQUIRED)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "gNjNtsCf3NjC"
      },
      "source": [
        "Evaluated references are re-evaluated on every call."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "height": 85
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 146,
          "status": "ok",
          "timestamp": 1528959274791,
          "user": {
            "displayName": "Dan Holtmann-Rice",
            "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s128",
            "userId": "110473418774139951293"
          },
          "user_tz": 240
        },
        "id": "jGxU4AQq3SfI",
        "outputId": "572ac1b3-5c8a-46a5-e03b-a93f54d715a7"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "arg = 3\n",
            "arg = 4\n",
            "arg = 1\n",
            "arg = 3\n"
          ]
        }
      ],
      "source": [
        "@gin.configurable\n",
        "def randint():\n",
        "  return random.randint(0, 10)\n",
        "\n",
        "gin.parse_config(\"\"\"\n",
        "print_arg.arg = @randint()\n",
        "\"\"\")\n",
        "print_arg(gin.REQUIRED)\n",
        "print_arg(gin.REQUIRED)\n",
        "print_arg(gin.REQUIRED)\n",
        "print_arg(gin.REQUIRED)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "zv2IpNj07hMI"
      },
      "source": [
        "### Scopes (configuring the same function in multiple ways)\n",
        "\n",
        "Bindings can be \"scoped\", using the `scope/fn_or_class.parameter = value` syntax. Such bindings are only in effect when the function is called with that scope active. References can be marked with a scope (`@scope/fn_or_class`) to force the associated function or class to be called within the specified scope."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "height": 51
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 174,
          "status": "ok",
          "timestamp": 1528959274994,
          "user": {
            "displayName": "Dan Holtmann-Rice",
            "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s128",
            "userId": "110473418774139951293"
          },
          "user_tz": 240
        },
        "id": "z0iKLBHQ7liE",
        "outputId": "fcd73465-db7d-4d1b-8bf2-1732b9184396"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "fn1() = 'scope_a'\n",
            "fn2() = 'scope_b'\n"
          ]
        }
      ],
      "source": [
        "@gin.configurable\n",
        "def return_arg(arg):\n",
        "  return arg\n",
        "\n",
        "@gin.configurable\n",
        "def call_fns(fn1, fn2):\n",
        "  print('fn1() = %r' % fn1())\n",
        "  print('fn2() = %r' % fn2())\n",
        "\n",
        "gin.parse_config(\"\"\"\n",
        "call_fns.fn1 = @scope_a/return_arg\n",
        "scope_a/return_arg.arg = 'scope_a'\n",
        "\n",
        "call_fns.fn2 = @scope_b/return_arg\n",
        "scope_b/return_arg.arg = 'scope_b'\n",
        "\"\"\")\n",
        "\n",
        "call_fns(gin.REQUIRED, gin.REQUIRED)"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "name": "gin_intro.ipynb"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
